package biz.datalk.industrialland.admin.modules.common.servie.impl;

import biz.datalk.industrialland.admin.config.properties.IndustrialLandCoreProperties;
import biz.datalk.industrialland.admin.modules.common.servie.IHolidayService;
import biz.datalk.industrialland.common.def.CommonConstant;
import biz.datalk.industrialland.common.def.enums.DBYNEnum;
import biz.datalk.industrialland.mbg.mapper.SysHolidayInfoMapper;
import biz.datalk.industrialland.mbg.pojo.po.SysHolidayInfo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author tarofang@163.com
 * @date 2024-01-22
 */
@Slf4j
@Service
public class HolidayServiceImpl implements IHolidayService {
    private final SysHolidayInfoMapper sysHolidayInfoMapper;
    private final IndustrialLandCoreProperties sharedGarageAdminProperties;

    public HolidayServiceImpl(SysHolidayInfoMapper sysHolidayInfoMapper, IndustrialLandCoreProperties sharedGarageAdminProperties) {
        this.sysHolidayInfoMapper = sysHolidayInfoMapper;
        this.sharedGarageAdminProperties = sharedGarageAdminProperties;
    }

    @Override
    public LocalDateTime plusDayEnd(LocalDateTime start, int plusDay) {
        if (plusDay <= 0) {
            log.debug("plusDay is zero.");
            return LocalDateTime.of(start.toLocalDate(), LocalTime.of(23, 59, 59));
        }
        // 从数据库中获取所有节假日
        LocalDateTime startLdt = LocalDateTime.of(start.getYear(), 1, 1, 0, 0, 0);
        LocalDateTime nextYearLdt = LocalDateTime.of(start.getYear() + 1, 12, 31, 0, 0, 0);
        String startYmd = startLdt.format(DateTimeFormatter.ofPattern(CommonConstant.FMT_YYYY_MM_DD));
        String endYmd = nextYearLdt.format(DateTimeFormatter.ofPattern(CommonConstant.FMT_YYYY_MM_DD));

        QueryWrapper<SysHolidayInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(SysHolidayInfo.COL_LEGAL_DATE, SysHolidayInfo.COL_IS_WORK_DAY);
        queryWrapper.between(SysHolidayInfo.COL_LEGAL_DATE, startYmd, endYmd);
        queryWrapper.eq(SysHolidayInfo.COL_IS_VALID, DBYNEnum.Y.name());
        queryWrapper.eq(SysHolidayInfo.COL_IS_DELETE, DBYNEnum.N.name());
        List<SysHolidayInfo> holidayInfos = sysHolidayInfoMapper.selectList(queryWrapper);
        holidayInfos = Optional.ofNullable(holidayInfos).orElse(Lists.newArrayList());
        if (CollectionUtils.isEmpty(holidayInfos)) {
            log.warn("Holiday information is not configured");
        }
        Map<String, Integer> holidayMap = this.convertMap(holidayInfos);

        // 每周的休息日
        List<Integer> offDayOfWeeks = sharedGarageAdminProperties.getOffDayOfWeeks();

        // 不计算今日
        LocalDateTime plusDayLdt = LocalDateTime.of(start.toLocalDate(), start.toLocalTime());
        int loopIdx = 0;
        String ymd;
        while (loopIdx < plusDay) {
            plusDayLdt = plusDayLdt.plusDays(1L);
            ymd = plusDayLdt.format(DateTimeFormatter.ofPattern(CommonConstant.FMT_YYYY_MM_DD));
            DayOfWeek dayOfWeek = plusDayLdt.getDayOfWeek();
            int value = dayOfWeek.getValue(); // from 1 (Monday) to 7 (Sunday)

            // 如果是周末，默认休息(休息日)，需要判定是否加班
            // 如果是周一到周五，默认上班(工作日)，需要判定是否休息
            int defaultIsWorkDay = offDayOfWeeks.contains(value) ? 0 : 1;
            // 如果当前日期是休息日, 则跳过
            Integer isWorkDay = holidayMap.getOrDefault(ymd, defaultIsWorkDay);
            if (isWorkDay == 0) {
                continue;
            }
            loopIdx++;
        }

        return LocalDateTime.of(plusDayLdt.toLocalDate(), LocalTime.of(23, 59, 59));
    }

    @Override
    public boolean isWorkday(LocalDateTime ldt) {
        Assert.notNull(ldt, "The 'ldt' must not be null.");

        String ymd = ldt.format(DateTimeFormatter.ofPattern(CommonConstant.FMT_YYYY_MM_DD));
        QueryWrapper<SysHolidayInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(SysHolidayInfo.COL_LEGAL_DATE, ymd);
        queryWrapper.eq(SysHolidayInfo.COL_IS_DELETE, DBYNEnum.N.name());
        queryWrapper.eq(SysHolidayInfo.COL_IS_VALID, DBYNEnum.Y.name());
        SysHolidayInfo sysHolidayInfo = sysHolidayInfoMapper.selectOne(queryWrapper);
        boolean isWorkday;
        if (sysHolidayInfo != null) {
            isWorkday = sysHolidayInfo.getIsWorkDay() == 1;
        } else {
            DayOfWeek dayOfWeek = ldt.getDayOfWeek();
            int value = dayOfWeek.getValue(); // from 1 (Monday) to 7 (Sunday)

            // 每周的休息日
            List<Integer> offDayOfWeeks = sharedGarageAdminProperties.getOffDayOfWeeks();
            isWorkday = !offDayOfWeeks.contains(value);
        }

        if (log.isInfoEnabled()) {
            log.info("isWorkday: {}, ymd: {}", isWorkday, ymd);
        }
        return isWorkday;
    }

    @Override
    public boolean isWorkday() {
        return this.isWorkday(LocalDateTime.now());
    }


    /**
     * key - 日期 yyyy-MM-dd
     * val - 0-休息日 1-工作日
     *
     * @param holidayInfos 节假日信息
     *
     * @return 节假日信息
     *
     * @author tarofang@163.com
     * @date 2024年01月22日
     */
    private Map<String, Integer> convertMap(List<SysHolidayInfo> holidayInfos) {
        Map<String, Integer> resMap = Maps.newHashMap();
        if (CollectionUtils.isEmpty(holidayInfos)) {
            return resMap;
        }
        String ymd;
        for (SysHolidayInfo holidayInfo : holidayInfos) {
            ymd = DateFormatUtils.format(holidayInfo.getLegalDate(), CommonConstant.FMT_YYYY_MM_DD);
            resMap.put(ymd, holidayInfo.getIsWorkDay());
        }
        return resMap;
    }
}
